package justweb

import (
	"context"
	"log"
	"sync"
	"time"
)

// goroutine池

type gPool struct {
	capacity int
	active   chan struct{} // 工作信号组
	task     chan *Task    // 正在提交的任务

	// 调度相关
	wg sync.WaitGroup // 处理任务上一道保险

	quit chan struct{} // 接收关闭信号

	// 增加控制单元
	preAlloc  bool // 是否创建的时候直接创建所有工作单位 true=直接创建
	waitOfNot bool // 工作单位满时是否阻塞等待 true=等待

}

var groutinePool *gPool // 使用懒汉模式创建单例
// var once sync.Once
var mu sync.Mutex

// 直接使用context
type Task struct {
	Run     func(context.Context) // 任务函数
	Timeout time.Duration         // 超时时间
	Error   chan error            // 任务返回的错误情况
	Cancel  context.CancelFunc    // 取消函数
	Done    <-chan struct{}       // 任务是否执行结束
	Data    interface{}           // 可以挂载数据
	Delay   func(interface{})     // 延迟绑定 -- 支持用户自己扩展
	// Delay   func(<-chan struct{}) // 延迟绑定 -- 支持用户自己扩展
}

// 创建池子
var (
	maxPools int = 1000
	minPools int = 1
)

type options func(o *gPool)

func WithpreAlloc(t bool) options {
	return func(o *gPool) {
		o.preAlloc = t
	}
}
func WithWaitOrNot(t bool) options {
	return func(o *gPool) {
		o.waitOfNot = t
	}
}

func NewGPool(cap int, option ...options) *gPool {
	// 单例创建
	if groutinePool == nil {
		mu.Lock()
		if groutinePool == nil { // 防止并发
			// 检查参数
			if cap > maxPools {
				cap = maxPools
			} else if cap < minPools {
				cap = minPools
			}

			groutinePool = &gPool{
				capacity:  cap,
				active:    make(chan struct{}, cap), // 调配workers
				task:      make(chan *Task),
				quit:      make(chan struct{}),
				preAlloc:  false, // 默认为false ， 不直接创建
				waitOfNot: true,  // 默认为会停下来等待
			}

			for i := 0; i < len(option); i++ {
				option[i](groutinePool)
			}

			// 如果提前创建，就在这里创建
			if groutinePool.preAlloc {
				for i := 0; i < groutinePool.capacity; i++ {
					groutinePool.NewWorker(i + 1)
					groutinePool.active <- struct{}{}
				}
			}

			go groutinePool.start()
		}
		mu.Unlock()
	} else {
		log.Println("Pool 重复创建。")
	}
	return groutinePool
}

// 销毁池子
func (pool *gPool) Free() {
	if groutinePool == nil {
		return
	}
	close(pool.quit)
	pool.wg.Wait()
	log.Println("Goroutine pool destory.")
	groutinePool = nil
}

// 获取池子
func GetGPool() (*gPool, bool) {
	if groutinePool != nil {
		return groutinePool, true
	}
	return nil, false
}

// goroutine池创建完需要开启，监控任务执行情况并且接收任务提交
func (pool *gPool) start() error {
	log.Println("successful new a goroutine pool with capacity : ", pool.capacity)
	id := -1

	if !pool.preAlloc { // 如果不要提前创建工作单位
		// 这里需要不断监听退出信号，又需要等待任务传递过来，有点互斥
	LOOP:
		for t := range pool.task {
			go func() { pool.task <- t }() // 将t归还 -- 开启一个协程执行会比较自洽

			select {
			case <-pool.quit:
				return nil
			case pool.active <- struct{}{}:
				id++
				pool.NewWorker(id)
			default:
				break LOOP
			}
		}
	}

	// 下面则继续监听与创建——这个是接合提前创建的模式
	for {
		select {
		case <-pool.quit: // 监听关闭信号
			return nil
		case pool.active <- struct{}{}: // 池内有空间
			id++
			pool.NewWorker(id)
		}
	}
}

// 创建工作单位 - 类似于自旋线程
func (pool *gPool) NewWorker(id int) {
	pool.wg.Add(1)

	go func() {
		defer func() {
			if err := recover(); err != nil { // 传入执行的函数有可能panic
				log.Printf("worker [_%d_] get a panic!!\n", id)
				<-pool.active
			}
			pool.wg.Done() // 处理结束就done掉
		}()

		log.Printf("worker [_%d_] wait a work ...\n", id)
		for {
			select {
			case <-pool.quit: // 是不是存在强制中断的问题?
				log.Printf("worker [_%d_] exit...\n", id)
				<-pool.active
				return
			case f := <-pool.task:
				log.Printf("worker [_%d_] get a work ...\n", id)

				ctx, cancel := context.WithTimeout(context.Background(), f.Timeout)
				f.Cancel = cancel

				// 启动一个协程监听是否cancel -- 这个部分还是可以优化的
				// go func(ctx context.Context) {
				// 	<-ctx.Done()
				// 	f.Error = ctx.Err()
				// 	// close(f.Done)
				// 	<-f.Done
				// }(ctx)

				// ！！！TODO 闭包延时绑定使用待测试 ！！！
				f.Delay(ctx.Done())

				errorDelay := func(e interface{}) { // 新的闭包，记录当前环境的err
					f.Error <- ctx.Err()
				}
				f.Delay = errorDelay

				f.Run(ctx) // 两种结果，要么执行完了，要么因为超时被取消了
				cancel()
			}
		}
	}()
}

var (
	DefaultTimeout time.Duration = time.Second * 1
)

// 选项模式
type taskOption func(*Task)

// 自定义超时时间
func WithTimeout(tout time.Duration) taskOption {
	return func(t *Task) {
		t.Timeout = tout
	}
}

// 选择挂载data内容
func WithData(data interface{}) taskOption {
	return func(t *Task) {
		t.Data = data
	}
}

// 新建任务对象，传入函数 -- 强制要求设置超时时间
func NewTask(f func(context.Context), timeout time.Duration, taskOp ...taskOption) *Task {
	ch := make(<-chan struct{})
	// var ch *<-chan struct{} = make(<-chan struct{})
	res := &Task{
		Run:     f,
		Timeout: timeout,
		Error:   make(chan error, 1),
		Done:    ch,
		Data:    nil,
	}

	delay := func(f interface{}) { // 闭包记住了当前的变量环境(后续调用闭包通过interface{}修改这里的内容)
		res.Done = f.(<-chan struct{})
		res.Error <- nil
	}

	res.Delay = delay

	for i := 0; i < len(taskOp); i++ {
		taskOp[i](res)
	}

	return res
}

// 到底需不需要一个调度器的结构?或者这部分功能可以集成起来?
// 接收任务-执行任务-完成通知  :  这些工作交给pool自己运行就可以了
// type schedule struct {}

// 提交任务调度
func (pool *gPool) Schedule(t *Task) error {
	select {
	case <-pool.quit:
		return ErrorGPoolClosed // 不再接收与提交任务
	case pool.task <- t: // 提交任务
		return nil
	default:
		if pool.waitOfNot { // 如果选择等待
			pool.task <- t
			return nil
		}
		return ErrorGPoolNoIdle
	}
}
